﻿using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Text;

namespace Ben.Http
{
    public class ClientRequest
    {
        private readonly List<byte> _buffer;
        private Action<byte> _fp;
        public string Method { get; private set; }
        public Dictionary<string, string> QueryString { get; private set; }
        public string Protocol { get; private set; }
        public string Path { get; private set; }
        public string RawUrl { get; private set; }

        public Dictionary<string, string> Headers { get; private set; }
        public Socket Client { get; private set; }
        public ClientRequest(Socket client)
        {
            Client = client;
            _buffer = new List<byte>();
            _fp = ReadMethod;
            Headers = new Dictionary<string, string>();
            var buffer = new byte[1];
            var timeout = DateTime.Now.Add(TimeSpan.FromSeconds(10));
            while (true)
            {
                if (client.Available < 1)
                {
                    if (timeout >= DateTime.Now) continue;
                    break;
                }
                var len = client.Receive(buffer, 0, 1, SocketFlags.None);
                if (len < 1)
                {
                    if (timeout >= DateTime.Now) continue;
                    break;
                }
                timeout = DateTime.Now.Add(TimeSpan.FromSeconds(10));
                _fp(buffer[0]);
                if (_fp == EndOfHeader) break;
            }
        }
        private void ReadMethod(byte value)
        {
            if (value != ' ')
            {
                _buffer.Add(value);
                return;
            }
            Method = Encoding.Default.GetString(_buffer.ToArray());
            _buffer.Clear();
            _fp = ReadUrl;
        }
        private void ReadUrl(byte value)
        {
            if (value != ' ')
            {
                _buffer.Add(value);
                return;
            }
            RawUrl = Encoding.Default.GetString(_buffer.ToArray());
            var index = RawUrl.IndexOf('?');
            QueryString = new Dictionary<string, string>();
            if (index < 0)
            {
                Path = RawUrl;
            }
            else
            {
                Path = RawUrl.Substring(0, index);

                var list = RawUrl.Substring(index + 1).Split('&');
                foreach (var i in list)
                {
                    if (string.IsNullOrEmpty(i)) continue;
                    var e = i.IndexOf('=');
                    if (e < 1 || e == i.Length - 1) continue;
                    QueryString[i.Substring(0, e)] = i.Substring(e + 1);
                }
            }
            _buffer.Clear();
            _fp = ReadProtocol;
        }
        private void ReadProtocol(byte value)
        {
            if (value == '\r') return;
            if (value != '\n')
            {
                _buffer.Add(value);
                return;
            }
            Protocol = Encoding.Default.GetString(_buffer.ToArray());
            _buffer.Clear();
            _fp = ReadHeaders;
        }
        private static void EndOfHeader(byte value){}
        private void ReadHeaders(byte value)
        {
            if (value == '\r') return;
            if (value != '\n')
            {
                _buffer.Add(value);
                return;
            }
            if (_buffer.Count < 1)
            {
                _fp = EndOfHeader;
                return;
            }
            var item = Encoding.Default.GetString(_buffer.ToArray());
            _buffer.Clear();
            var index = item.IndexOf(": ");
            if (index > 0)
                Headers[item.Substring(0, index)] = item.Substring(index + 2);
            else
                Headers[item] = string.Empty;
        }

        public void ResponseStatus(HttpStatusCodes status)
        {
            switch (status)
            {
                case HttpStatusCodes.RedirectionMovedPermanently    : Client.Send(HttpStatusByteCode.RedirectionMovedPermanently    ); break;
                case HttpStatusCodes.RedirectionMovedTemporarily    : Client.Send(HttpStatusByteCode.RedirectionMovedTemporarily    ); break;
                case HttpStatusCodes.RedirectionSeeOther            : Client.Send(HttpStatusByteCode.RedirectionSeeOther            ); break;
                case HttpStatusCodes.RedirectionNotModified         : Client.Send(HttpStatusByteCode.RedirectionNotModified         ); break;
                case HttpStatusCodes.ClientErrorBadRequest          : Client.Send(HttpStatusByteCode.ClientErrorBadRequest          ); break;
                case HttpStatusCodes.ClientErrorUnauthorized        : Client.Send(HttpStatusByteCode.ClientErrorUnauthorized        ); break;
                case HttpStatusCodes.ClientErrorForbidden           : Client.Send(HttpStatusByteCode.ClientErrorForbidden           ); break;
                case HttpStatusCodes.ClientErrorNotFound            : Client.Send(HttpStatusByteCode.ClientErrorNotFound            ); break;
                case HttpStatusCodes.ServerErrorInternalServerError : Client.Send(HttpStatusByteCode.ServerErrorInternalServerError ); break;
                case HttpStatusCodes.ServerErrorNotImplemented      : Client.Send(HttpStatusByteCode.ServerErrorNotImplemented      ); break;
                case HttpStatusCodes.ServerErrorBadGateway          : Client.Send(HttpStatusByteCode.ServerErrorBadGateway          ); break;
                case HttpStatusCodes.ServerErrorServiceUnavailable  : Client.Send(HttpStatusByteCode.ServerErrorServiceUnavailable  ); break;
            }
        }
    }
    public enum HttpStatusCodes
    {
        RedirectionMovedPermanently,
        RedirectionMovedTemporarily,
        RedirectionSeeOther,
        RedirectionNotModified,
        ClientErrorBadRequest,
        ClientErrorUnauthorized,
        ClientErrorForbidden,
        ClientErrorNotFound,
        ServerErrorInternalServerError,
        ServerErrorNotImplemented,
        ServerErrorBadGateway,
        ServerErrorServiceUnavailable,

    }
    public static class HttpStatusByteCode
    {
        public static readonly byte[] RedirectionMovedPermanently              = Encoding.UTF8.GetBytes("HTTP/1.0 300 Moved Permanently    \r\nContent-Length: 0\r\nContent-Type: text/html\r\n\r\n");
        public static readonly byte[] RedirectionMovedTemporarily              = Encoding.UTF8.GetBytes("HTTP/1.0 301 Moved Temporarily    \r\nContent-Length: 0\r\nContent-Type: text/html\r\n\r\n");
        public static readonly byte[] RedirectionSeeOther                      = Encoding.UTF8.GetBytes("HTTP/1.0 302 See Other            \r\nContent-Length: 0\r\nContent-Type: text/html\r\n\r\n");
        public static readonly byte[] RedirectionNotModified                   = Encoding.UTF8.GetBytes("HTTP/1.0 304 Not Modified         \r\nContent-Length: 0\r\nContent-Type: text/html\r\n\r\n");
        public static readonly byte[] ClientErrorBadRequest                    = Encoding.UTF8.GetBytes("HTTP/1.0 400 Bad Request          \r\nContent-Length: 0\r\nContent-Type: text/html\r\n\r\n");
        public static readonly byte[] ClientErrorUnauthorized                  = Encoding.UTF8.GetBytes("HTTP/1.0 401 Unauthorized         \r\nContent-Length: 0\r\nContent-Type: text/html\r\n\r\n");
        public static readonly byte[] ClientErrorForbidden                     = Encoding.UTF8.GetBytes("HTTP/1.0 403 Forbidden            \r\nContent-Length: 0\r\nContent-Type: text/html\r\n\r\n");
        public static readonly byte[] ClientErrorNotFound                      = Encoding.UTF8.GetBytes("HTTP/1.0 404 Not Found            \r\nContent-Length: 0\r\nContent-Type: text/html\r\n\r\n");
        public static readonly byte[] ServerErrorInternalServerError           = Encoding.UTF8.GetBytes("HTTP/1.0 500 InternalServerError  \r\nContent-Length: 0\r\nContent-Type: text/html\r\n\r\n");
        public static readonly byte[] ServerErrorNotImplemented                = Encoding.UTF8.GetBytes("HTTP/1.0 501 NotImplemented       \r\nContent-Length: 0\r\nContent-Type: text/html\r\n\r\n");
        public static readonly byte[] ServerErrorBadGateway                    = Encoding.UTF8.GetBytes("HTTP/1.0 502 BadGateway           \r\nContent-Length: 0\r\nContent-Type: text/html\r\n\r\n");
        public static readonly byte[] ServerErrorServiceUnavailable            = Encoding.UTF8.GetBytes("HTTP/1.0 503 ServiceUnavailable   \r\nContent-Length: 0\r\nContent-Type: text/html\r\n\r\n");
    }
}